import os
import hashlib
import threading
import traceback

import asyncio
import requests
import sqlite3
import datetime
from tkinter.messagebox import showerror

from concurrent.futures import ThreadPoolExecutor
from tkinter import *
from tkinter import filedialog
from tkinter.ttk import Progressbar, Notebook
from login import qqmail_login

conn = sqlite3.connect('.server.db')
conn.execute('''
CREATE TABLE IF NOT EXISTS server
(
    id INT PRIMARY KEY,
    host VARCHAR
);''')
conn.execute('''
CREATE TABLE IF NOT EXISTS qqemail
(
    id INT PRIMARY KEY,
    account VARCHAR,
    password VARCHAR,
    lasttime DATETIME
);
''')
conn.execute('''
CREATE UNIQUE INDEX IF NOT EXISTS qqemail_account_uindex ON qqemail (account);
''')


class DB:

    def __enter__(self):
        self.c = conn.cursor()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

    def query(self, sql, *args):
        self.c.execute(sql, args)
        return self.c.fetchall()

    def queryone(self, sql, *args):
        self.c.execute(sql, args)
        return self.c.fetchone()

    def execute(self, sql, *args):
        self.c.execute(sql, args)
        conn.commit()


class Common:

    def __init__(self):
        with DB() as db:
            self._host = "http://47.107.62.237:8081"
            self._api_video_add = self._host + "/qqemail/video/add/v2"
            self._api_cookie_add = self._host + "/qqemail/cookie/add"

    def video_add(self, name, size, md5, sha1):
        data = requests.post(self._api_video_add, data={
            "name": name,
            "size": size,
            "md5": md5,
            "sha1": sha1,
        }).json()

        if "url" in data:
            data["url"] = self._host + data["url"]

        return data

    def cookie_add(self, data):

        if data:
            return requests.post(self._api_cookie_add, data=data).json()
        raise Exception("登录失败")


class Application(Tk):

    def __init__(self, *args, **kwargs):
        super(Application, self).__init__(*args, **kwargs)

        self.title("连接服务器")

        # ConnFrame(self.main).pack()
        self.main()

    def main(self):
        self.title("微云助手v1.0")
        self.geometry("600x500")

        tabControl = Notebook(self)
        tabControl.pack(expand=1, fill="both")

        tab1 = Frame(tabControl)
        tab2 = Frame(tabControl)
        tab3 = Frame(tabControl)

        tabControl.add(tab1, text='文件解析')
        tabControl.add(tab2, text='邮箱登录')
        tabControl.add(tab3, text='自动登录')

        FileFrame(tab1).pack()
        QQEmailFrame(tab2).pack()
        LoginFrame(tab3).pack()


class LoginFrame(Frame):

    def __init__(self, *args, **kwargs):
        super(LoginFrame, self).__init__(*args, **kwargs)
        self.pool = ThreadPoolExecutor()
        self.btn = Button(self, text='开启自动登录', fg='green', command=self._start)
        self.btn.pack(pady=20)

        self.status = False
        self.taskCount = 0

        self.loop = asyncio.get_event_loop()

        with DB() as db:
            self._host = db.queryone("SELECT host FROM server WHERE id=1")[0]
            self._api_cookie_add = self._host + "/qqemail/cookie/add"

    def _start(self):

        if self.status:

            self.btn.configure(state=DISABLED)
            self.status = False

        else:
            with DB() as db:
                emails = db.query("SELECT account,password FROM qqemail;")

                if self.taskCount <= 0:
                    def _func():
                        self.loop.run_until_complete(self._login(emails))

                    self.btn.configure(text='关闭自动登录', fg='red')
                    self.status = True

                    threading.Thread(target=_func).start()

    async def _login(self, emails):

        self.taskCount += 1

        def _func(account, password):
            try:
                data = qqmail_login(account, password)
                if data:
                    resp = requests.post(self._api_cookie_add, data=data).json()
                    print(datetime.datetime.now(), resp)
            except:
                traceback.print_exc()

        count, next = 0, 0
        while self.status:

            if count >= next:
                next += 7200
                for e in emails:
                    self.pool.submit(_func, e[0], e[1])

            await asyncio.sleep(5)
            count += 5

        self.taskCount -= 1

        self.btn.configure(text='开启自动登录', fg='green')
        self.btn.configure(state=NORMAL)

    def destroy(self):
        super(LoginFrame, self).destroy()
        self.status = False


class ConnFrame(Frame):

    def __init__(self, success, *args, **kwargs):
        super(ConnFrame, self).__init__(*args, **kwargs)

        self.success = success

        self.lable = Label(self, text="服务器:")
        self.status = Label(self, text="  ", fg='red')

        sv = StringVar()
        sv.trace("w", lambda name, index, mode, sv=sv: self.status.configure(text='  '))
        self.entry = Entry(self, textvariable=sv)

        self.btn = Button(self, text='连接', command=self._connect)

        self.lable.pack(padx=5, pady=10, side=LEFT)
        self.entry.pack(padx=5, pady=10, side=LEFT)
        self.status.pack(padx=5, pady=10, side=LEFT)
        self.btn.pack(padx=5, pady=10, side=LEFT)

        self._load()

    def _load(self):

        with DB() as db:
            o = db.queryone('SELECT * FROM server WHERE id=1')
            if not o:
                o = (1, "")
                db.execute("INSERT INTO server(id,host) VALUES (?,?)", 1, "")
                conn.commit()

        self.entry.insert(END, o[1])

    def _connect(self):

        server = self.entry.get()

        with DB() as db:
            db.execute("UPDATE server SET host=? WHERE id=?", server, 1)

        try:

            data = requests.post(server, timeout=3).json()

            print(datetime.datetime.now(), "连接服务器:", data)

            if data['data'] == "success":
                self.destroy()
                self.success()
        except:
            traceback.print_exc()
            self.status.configure(text=' 连接失败 ')


class FileFrame(LabelFrame):

    def __init__(self, *args):
        super(FileFrame, self).__init__(*args, text=' 文件解析 ')

        self.pool = ThreadPoolExecutor()

        self.top = Frame(self)
        self.center = Frame(self)
        self.footter = Frame(self)

        self.top.pack()
        self.center.pack()
        self.footter.pack()

        self._items = []
        self._copy = None

        Button(self.top, text="选择文件", command=self._select_file).pack()

        with DB() as db:
            self.server = db.queryone("SELECT host FROM server WHERE id=1")[0]

    def _select_file(self):

        self._clean()

        filenames = filedialog.askopenfilenames(filetypes=(("MP4 文件", "*.mp4"),))

        for i, file in enumerate(filenames):
            item = FileItem(self.center, file, i)
            self._items.append(item)
            self.pool.submit(item)

        if filenames:
            self._copy = Button(self.footter, text="复制全部", command=self._copy_all)
            self._copy.pack(pady=15)

    def _clean(self):

        if self._copy:
            self._copy.destroy()
            self._copy = None

        for item in self._items:
            item.destroy()

        self._items[:] = []

    def _copy_all(self):
        string = "\n".join([i.url for i in self._items if i.url])
        self.clipboard_clear()
        self.clipboard_append(string)


class QQEmailFrame(LabelFrame):

    def __init__(self, *args):
        super(QQEmailFrame, self).__init__(*args, text=" QQ邮箱 ")

        self.items = []

        self.top = Frame(self)
        self.center = Frame(self)

        self.top.pack()
        self.center.pack()

        Label(self.top, text='账号:').pack(padx=5, pady=10, side=LEFT)
        self.account = Entry(self.top)
        self.account.pack(padx=5, pady=10, side=LEFT)

        Label(self.top, text='密码:').pack(padx=5, pady=10, side=LEFT)
        self.password = Entry(self.top)
        self.password.pack(padx=5, pady=10, side=LEFT)

        Button(self.top, text='添加', command=self._add).pack(padx=5, pady=10, side=LEFT)

        with DB() as db:
            self.server = db.queryone("SELECT host FROM server WHERE id=1")[0]

        Label(self.center, text="账号").grid(row=0, column=0)
        Label(self.center, text="密码").grid(row=0, column=1)
        Label(self.center, text="登录时间").grid(row=0, column=2)
        Label(self.center, text="测试状态").grid(row=0, column=3)

        self._load()

    def _add(self):
        account = self.account.get()
        password = self.password.get()

        if not account or not password:
            showerror("错误", "账号密码不能为空!", parent=self)
            return

        with DB() as db:
            o = db.queryone('SELECT * FROM qqemail WHERE account=?', account)
            if o:
                showerror("错误", "该账号已存在! 先删除后再添加", parent=self)
                return

            db.execute('INSERT INTO qqemail(account,password) VALUES (?,?)', account, password)

        self._load()

    def _load(self):

        for i in self.items:
            i.destroy()

        self.items[:] = []

        with DB() as db:
            for i, hit in enumerate(db.query('SELECT account,password,lasttime FROM qqemail;')):
                self.items.append(QQEmailItem(self.center, hit[0], hit[1], hit[2], i + 1))


class QQEmailItem(Common):

    def __init__(self, frame, account, password, time, rownum):
        super(QQEmailItem, self).__init__()

        self._account = account
        self._password = password
        self.frame = frame

        self.account = Label(frame, text=account)
        self.password = Label(frame, text=password)
        self.time = Label(frame, text=' {} '.format(time), fg='green')
        self.status = Label(frame, text='  ', fg='red')
        self.test = Button(frame, text=' 测试 ', fg='green', command=self._test)
        self.btn = Button(frame, text=' 删除 ', fg='red', command=self._del)

        self.account.grid(row=rownum, column=0)
        self.password.grid(row=rownum, column=1)
        self.time.grid(row=rownum, column=2)
        self.status.grid(row=rownum, column=3)
        self.test.grid(row=rownum, column=4)
        self.btn.grid(row=rownum, column=5)

    def _test(self):
        self.test.configure(state=DISABLED)
        self.status.configure(text=' 正在测试 ')

        try:
            data = qqmail_login(self._account, self._password)
            result = self.cookie_add(data)

            if result:
                now = datetime.datetime.now()

                self.time.configure(text=' {} '.format(now))
                self.status.configure(text=' 成功 ', fg='green')

                with DB() as db:
                    db.execute("UPDATE qqemail SET lasttime=? WHERE account=?", now, self._account)

        except:
            traceback.print_exc()
            self.status.configure(text=' 失败 ')

        finally:
            self.test.configure(state=NORMAL)

    def _del(self):

        with DB() as db:
            db.execute('DELETE FROM qqemail WHERE account=?;', self._account)
            self.destroy()

    def destroy(self):
        self.account.destroy()
        self.password.destroy()
        self.btn.destroy()
        self.test.destroy()
        self.time.destroy()


class FileItem(Common):

    def __init__(self, frame, path, row):

        super(FileItem, self).__init__()

        self.url = None
        self.path = path
        self.name = os.path.basename(path)

        self.frame = frame

        self.label = Label(frame, text=self.name)
        self.progress = Progressbar(frame, maximum=1, orient="horizontal", length=200, mode="determinate")
        self.status = Label(frame, fg='green')
        self.btn = Button(frame, text="入库", state=DISABLED, command=self._copy)

        self.label.grid(row=row, column=0)
        self.progress.grid(row=row, column=1)
        self.status.grid(row=row, column=2)
        self.btn.grid(row=row, column=3)

    def __call__(self, *args, **kwargs):

        try:
            self.status.configure(text=" 正在计算 ")
            md5, sha1 = self._cal()
            self.status.configure(text=" 正在解析 ")

            resp = self.video_add(self.name, self.size, md5, sha1)

            if "error" in resp:
                self.status.configure(text=" {} ".format(resp["error"]), fg='red')

            else:
                self.status.configure(text=" 成功 ")
                self.btn.configure(state=NORMAL)
                self.url = resp["url"]

        except:
            traceback.print_exc()
            self.status.configure(text="解析失败", fg='red')

    def destroy(self):
        self.label.destroy()
        self.progress.destroy()
        self.status.destroy()
        self.btn.destroy()

    def _cal(self):
        self.size = os.stat(self.path).st_size

        md5 = hashlib.md5()
        sha1 = hashlib.sha1()

        def change(value):
            self.progress['value'] = value

        with open(self.path, "rb") as f:
            buf = 1024 * 1024
            count = 0

            for chunk in iter(lambda: f.read(buf), b""):
                count += len(chunk)

                md5.update(chunk)
                sha1.update(chunk)

                change(count / self.size)

            return md5.hexdigest(), sha1.hexdigest()

    def _copy(self):
        data = requests.post("https://www.sowant.cc/api/movie/add", data={
            "name": self.name,
            "url": self.url,
            "public_time": datetime.datetime.now().strftime("%Y-%m-%d")
        }).json()

        print(data)


if __name__ == '__main__':
    win = Application()
    mainloop()

# pyinstaller.exe --noconsole --onefile --upx-dir=./ .\main.py
